﻿using FireflySoft.RateLimit.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FireflySoft.RateLimit.AspNetCore
{
    /// <summary>
    /// Rate Limit Middleware for multiple algorithm
    /// </summary>
    public class MultiRateLimitMiddleware : BaseRateLimitMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly HttpErrorResponse _error;
        private readonly HttpInvokeInterceptor _interceptor;
        private readonly IAlgorithm _algorithm;

        /// <summary>
        /// Create a new instance
        /// </summary>
        /// <param name="next"></param>
        /// <param name="error"></param>
        /// <param name="interceptor"></param>
        /// <param name="algorithm"></param>
        public MultiRateLimitMiddleware(RequestDelegate next, HttpErrorResponse error, HttpInvokeInterceptor interceptor, IAlgorithm algorithm)
            : base(error, interceptor)
        {
            _next = next;
            _error = error;
            _interceptor = interceptor;
            _algorithm = algorithm;
        }

        /// <summary>
        /// Asynchronous processing of Middleware
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {
            await DoOnBeforeCheck(context, _algorithm).ConfigureAwait(false);
            var checkResult = await _algorithm.CheckAsync(context).ConfigureAwait(false);
            SetRateLimitResultHeader(context, checkResult);
            await DoOnAfterCheck(context, checkResult).ConfigureAwait(false);

            if (checkResult.IsLimit)
            {
                await DoOnTriggered(context, checkResult).ConfigureAwait(false);

                context.Response.StatusCode = _error.HttpStatusCode;

                var headers = await BuildHttpHeaders(context, checkResult).ConfigureAwait(false);
                if (headers != null && headers.Count > 0)
                {
                    foreach (var h in headers)
                    {
                        context.Response.Headers.Append(h.Key, h.Value);
                    }
                }

                string content = await BuildHttpContent(context, checkResult).ConfigureAwait(false);
                if (!string.IsNullOrWhiteSpace(content))
                {
                    var bodyContent = Encoding.UTF8.GetBytes(content);
                    await context.Response.Body.WriteAsync(bodyContent, 0, bodyContent.Length).ConfigureAwait(false);
                }
                else
                {
                    await context.Response.WriteAsync(string.Empty).ConfigureAwait(false);
                }
            }
            else
            {
                await DoOnBreforUntriggeredDoNext(context, checkResult).ConfigureAwait(false);

                if (checkResult.RuleCheckResults.Any())
                {
                    await DoLeakyBucketWait(checkResult).ConfigureAwait(false);
                }

                await _next(context).ConfigureAwait(false);

                await DoOnAfterUntriggeredDoNext(context, checkResult).ConfigureAwait(false);
            }
        }
    }

    /// <summary>
    /// Rate Limit Middleware Extensions
    /// </summary>
    public static class MultiRateLimitMiddlewareExtensions
    {
        /// <summary>
        /// Using rate limit processor
        /// </summary>
        /// <param name="builder"></param>
        /// <returns></returns>
        public static IApplicationBuilder UseMultiRateLimit(this IApplicationBuilder builder)
        {
            var algorithms = MultiAlgorithmManager.GetAlgorithmInstances();
            foreach(var algo in algorithms)
            {
                builder.UseMiddleware<MultiRateLimitMiddleware>(algo);
            }

            return builder;
        }
    }
}
